package demo.topic12.reflection;


import bean.Dog;
import org.junit.Test;

import java.lang.reflect.*;

/**
 * 反射与内省
 * 一、反射（Relection）
 * 1. 概念：Java反射机制是在运行状态中，对于任意一个类，都能知道这个类的所有属性和方法；对于任意一个对象，
 * 都能够调用它的任意一个方法和属性，这种动态获取的信息以及动态调用对象方法的功能成为反射。
 * 2. 类信息->对象，对象->类信息。
 * 3. Class类
 * （a）Class类是一切的反射根源。
 * （b）得到Class类的对象有三种方式：
 * （b.1）Object类的getClass()方法。
 * （b.2）类.class。
 * （b.3）通过Class类的forName()方法。
 * （c）使用Class类进行对象的实例化操作
 * （c.1）调用无参构造进行实例化：public T newInstance() throws Instantiation Exception,IllegalAccessException
 * （c.2）调用有参构造进行实例化：public Constructor<?>[] getConstructors() throws SecurityException
 * （d）通过Class类取得类信息
 * （d.1）取得类所在的包：
 * （d.1.1）得到一个类所在的包：public Package getPackage()
 * （d.1.2）得到名字：public String getName()
 * （d.2）取得一个类中的全部方法：
 * （d.2.1）public Method[] getMethods()
 * （d.2.2）还原修饰符：public int getModifiers()
 * （d.2.3）public Class<?> getReturnTypes()
 * （d.2.4）public Class<?> getExceptionTypes()
 * （d.2.5）public static String toString(int mod)
 * （d.3）取得一个类中的全部属性：
 * （d.3.1）public Field[] getFields()
 * （d.3.2）public Field[] getDeclaredFields()
 * （d.3.3）public Class<?> getType()
 * （d.3.4）public String getName()
 * （e）通过Class类调用属性或方法
 * （e.1）调用类中的方法：
 * （e.1.1）public Object invoke(Object obj, Object... args)
 * （e.2）直接调用属性
 * （e.2.1）取得属性：public Object get(Object obj)
 * （e.2.2）设置属性：public void set(Object obj, Object value)
 * （e.2.3）设置属性外部可见：public void setAccessible(boolean flag)
 */
public class ReflectionDemo {

    /**
     * 获取Class类的三种形式
     */
    @Test
    public void test1() {
        // 1. 通过对象的getClass()方法获取
        Dog dog = new Dog("旺财", 4, "黄色");
        Class dogClass1 = dog.getClass();

        // 2. 通过 类.class
        Class dogClass2 = Dog.class;

        // 3. 通过 Class.forName 方法
        try {
            Class dogClass3 = Class.forName("bean.Dog");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    /**
     * 通过反射来实例化对象
     */
    @Test
    public void test2() {
        Class<Dog> dogClass = Dog.class;
        try {
            // 通过Class对象实例化对象，调用了默认无参构造方法
            Dog dog = (Dog)dogClass.newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取所有的构造方法
     */
    @Test
    public void test3() {
        Class<Dog> dogClass = Dog.class;
        Constructor<?>[] constructors = dogClass.getConstructors();
        for (int i = 0; i < constructors.length; i++) {
            System.out.println(constructors[i].getName());
            System.out.println(constructors[i].getParameterCount());
        }
        try {
            // 获取一个指定的构造方法
            Constructor<Dog> constructor = dogClass.getConstructor(String.class,  int.class, String.class);
            // 调用带参数的构造器，来实例化对象
            constructor.newInstance("大黄狗", 3, "黄色");
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取所有的属性
     */
    @Test
    public void test4() {
        Class<Dog> dogClass = Dog.class;
        // getFields：获取非私有属性
        Field[] fields = dogClass.getFields();
        // getDeclaredFields：获取所有属性，包括私有属性
        Field[] declaredFields = dogClass.getDeclaredFields();
        for (int i = 0; i < declaredFields.length; i++) {
            int modifiers = declaredFields[i].getModifiers();
            /*
                private name
                private age
                private color
             */
            System.out.println(Modifier.toString(modifiers) + " " +declaredFields[i].getName());
        }
    }

    /**
     * 获取类的信息
     */
    @Test
    public void test5() {
        Dog dog = new Dog("小奶狗", 1, "黄色");

        Class<Dog> dogClass = Dog.class;
        // 获取类的包名
        Package $package = dogClass.getPackage();
        System.out.println($package.getName());
        // 获取公共的方法，包阔继承的公有方法
        Method[] methods = dogClass.getMethods();
        for (int i = 0; i < methods.length; i++) {
            System.out.println(methods[i]);
            if(methods[i].getName().equals("toString")){
                try {
                    methods[i].invoke(dog);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                }
            }
        }
        // 访问私有方法，获取到本类中定义的所有方法（不包括父类）
        Method[] declaredMethods = dogClass.getDeclaredMethods();
        for (int i = 0; i < declaredMethods.length; i++) {
            System.out.println(declaredMethods[i]);
            if(declaredMethods[i].getName().equals("set")) {
                // 设置私有方法可以被访问（去除访问修饰符的检查）
                declaredMethods[i].setAccessible(true);
                try {
                    String str = (String)declaredMethods[i].invoke(dog);
                    System.out.println(str);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                }
            }
        }

    }
}
